package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"

	"github.com/minph/conf"
	"github.com/minph/fs"
	"github.com/minph/pool"
)

// AppConfig 读取配置文件
var AppConfig = conf.New("app.conf")

// ForceConfigInt 强制获取数值配置
func ForceConfigInt(groupName, itemName string) int {
	str := AppConfig.GetValue(groupName, itemName)
	value, err := strconv.Atoi(str)
	if err != nil {
		fmt.Println(err)
		return 0
	}

	return value
}

// Config 配置
type Config struct {
	To      int    // 终点
	From    int    // 起点
	URL     string // 请求地址
	File    string // 保存文件路径
	Routine int    // 开启协程数
}

// NewConfig 获取配置数据
func NewConfig(groupName string) *Config {
	return &Config{
		To:      ForceConfigInt(groupName, "to"),
		From:    ForceConfigInt(groupName, "from"),
		Routine: ForceConfigInt(groupName, "routine"),
		URL:     AppConfig.GetValue(groupName, "url"),
		File:    AppConfig.GetValue(groupName, "file"),
	}
}

// AppRun 开始任务
func AppRun(taskName string) {

	fmt.Println("执行协程任务 => ", taskName)

	// 任务计数
	counter := 0

	// 初始化配置
	c := NewConfig(taskName)

	//创建数据通道
	data := make(chan string)

	// 检查输出文件
	file := fs.NewFile(c.File)
	if !file.Exist() {
		content := fmt.Sprintf("{\"file\":\"%v\",\"data\":[\n", c.File)
		_ = file.Create()
		_ = file.AppendString(content)
	}

	// 新建协程
	app := pool.New(c.Routine).SetTask(c.From, c.To)

	app.OnceDone(func(a *pool.App) {
		fmt.Println("已结束一个协程，目前剩余数量：", a.Counter())
	})

	// 运行协程
	app.Run(func(a *pool.App, index int) {

		// 获取子任务
		from, to := a.Task(index)
		fmt.Printf("开启协程[%v]，数据范围(%v,%v)\n", index+1, from, to)

		// 开启循环
		for i := from; i <= to; i++ {

			// 发起请求
			query := fmt.Sprintf(c.URL, i)
			res, err := http.Get(query)
			if err != nil {
				fmt.Println(err)
				continue
			}

			//拦截 400/500 请求
			if res.StatusCode > 302 {
				continue
			}

			// 获取响应
			content, err := ioutil.ReadAll(res.Body)
			if err != nil {
				fmt.Println(err)
				continue
			}

			// 写入数据到主通道
			data <- string(content)

			// 关闭响应
			err = res.Body.Close()
			if err != nil {
				fmt.Println(err)
				continue
			}
		}
	}, func(a *pool.App) {
		// 读取通道数据
		content := <-data

		// 写入文件
		err := fs.AppendString(c.File, content+",\n")
		if err != nil {
			fmt.Println(err)
			return
		}

		// 打印日志
		counter++
		fmt.Printf("成功储存%v条数据\n", counter)
	})

	// 开启行过滤
	filter := AppConfig.GetValue(taskName, "filter")
	if len(filter) > 0 {

		fmt.Println("正在进行文件过滤")
		fmt.Printf("对包含[%v]的行将被过滤\n", filter)

		rows, err := file.ReadByRow() // 读取每行
		if err != nil {
			fmt.Println(err)
			return
		}

		file.Rewrite("") // 重写文件
		for _, row := range rows {
			if strings.Index(row, filter) == -1 {
				file.AppendString(row)
			}
		}
	}

	// 写入文件末尾
	err := file.AppendString("{}]}")
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println("所有任务结束")
}

func main() {

	groupName := AppConfig.GetValue("main", "task")
	appendix := AppConfig.GetValue("main", "appendix")

	if len(appendix) > 2 {
		file := fs.NewFile(AppConfig.GetValue(groupName, "file"))
		file.AppendString("{}]}")
		fmt.Println("文件尾部信息补充完成")
	} else {
		AppRun(groupName)
	}

	fmt.Scanln()
}
